﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Data.Linq;
using CoolCode.Data.Linq;
using CoolCode.Reflection;

namespace CoolCode.Data.Xml
{
    internal class TableProvider
    {
        private Dictionary<Type, XTable> Tables = new Dictionary<Type, XTable>();
        /// <summary>
        ///  Max Cache Time(unit: minute)
        /// </summary>
        private const int MaxCacheTime = 60 * 24;

        public TableProvider()
        {
        }

        //public Table<T> GetTable<T>(IDataContext context)
        //{
        //    var type = typeof(T);
        //    if (Tables.Keys.Contains(type))
        //    {
        //        IXmlTable t;
        //        var xt = Tables[type];
        //        t = xt.XmlTable;
        //        if (t == null)
        //            t = CreateTable<T>(context);
        //        xt.LastVisitDate = DateTime.Now;
        //        return (Table<T>)t;
        //    }
        //    else
        //    {
        //        var t = CreateTable<T>(context);
        //        string fileName = GetFileName(context.ConnectionString, t.ClassName);
        //        Tables.Add(type, new XTable { XmlTable = t, LastVisitDate = DateTime.Now, FileName = fileName, Root = t.Root });

        //        return t;
        //    }

        //}


        public Repository<T> GetReposity<T>(IDataContext context) where T : class 
        {
            var type = typeof(T);
            if (Tables.Keys.Contains(type))
            {
                IXmlTable t;
                var xt = Tables[type];
                t = xt.XmlTable;
                if (t == null)
                    t = CreateTable<T>(context);
                xt.LastVisitDate = DateTime.Now;
                return (Repository<T>)t;
            }
            else
            {
                var t = CreateTable<T>(context);
                string fileName = GetFileName(context.ConnectionString, t.ClassName);
                Tables.Add(type, new XTable { XmlTable = t, LastVisitDate = DateTime.Now, FileName = fileName, Root = t.Root });

                return t;
            }

        }

        public IXmlTable GetTable (IDataContext context,Type type)
        { 
            if (Tables.Keys.Contains(type))
            {
                IXmlTable t;
                var xt = Tables[type];
                t = xt.XmlTable;
                if (t == null)
                    t = CreateTable (context,type );
                xt.LastVisitDate = DateTime.Now;
                return t;
            }
            else
            {
                var t = CreateTable(context, type);
                string fileName = GetFileName(context.ConnectionString, t.ClassName);
                Tables.Add(type, new XTable { XmlTable = t, LastVisitDate = DateTime.Now, FileName = fileName, Root = t.Root });

                return t;
            }

        }


        private string GetFileName(string connectionString, string className)
        {
            string path = connectionString.EndsWith(@"\") ? connectionString : connectionString + @"\";
            return path + className + ".xml"; ;
        }

        public Dictionary<Type, XTable> GetAllTables()
        {
            return Tables;
        }

        /// <summary>
        /// Remove the table where last visited date less then<value> MaxCacheTime</value>
        /// </summary>
        public void Curtail()
        {
            int count = Tables.Count;
            List<Type> removeList = new List<Type>();
            var en = Tables.GetEnumerator();
            while (en.MoveNext())
                if (en.Current.Value.LastVisitDate.AddMinutes(MaxCacheTime) < DateTime.Now)
                    removeList.Add(en.Current.Key);
            foreach (var t in removeList)
                Tables.Remove(t);

        }

        //public Table<T> CreateTable<T>(IDataContext context)
        //{
        //    string className = BLReflection.GetClassName<T>();
        //    string fileName = GetFileName(context.ConnectionString, className);
        //    return new Table<T>(context) { ClassName = className, Root = Load(fileName), IsChanged = false };
        //}

        public Repository<T> CreateTable<T>(IDataContext context) where T:class 
        {
            string className = BLReflection.GetClassName<T>();
            string fileName = GetFileName(context.ConnectionString, className);
            return new Repository<T>(context) { ClassName = className, Root = Load(fileName), IsChanged = false };
        }

        public IXmlTable CreateTable(IDataContext context, Type t)
        {
            string className = t.Name;
            string fileName = GetFileName(context.ConnectionString, className);
            var instance = Activator.CreateInstance(typeof(Table<>).MakeGenericType(t), new object[] { context });
            var table = instance as IXmlTable;
            table.ClassName = className;
            table.Root = Load(fileName);
            table.IsChanged = false;
            return table;
        }


        public XElement Load(string fileName)
        {
            XDocument xd;
            if (!System.IO.File.Exists(fileName))
            {
                xd = XDocument.Parse("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
                       "<Sheet CreateDate=\"" + System.DateTime.Now + "\">" +
                        "</Sheet>"
                   );
            }
            else
                xd = XDocument.Load(fileName);
            return xd.Element("Sheet");
        }

        private static TableProvider _instance;

        public static TableProvider Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new TableProvider();
                }
                return _instance;
            }
        }

        internal class XTable
        {
            internal IXmlTable XmlTable { get; set; }
            internal DateTime LastVisitDate { get; set; }
            internal string FileName { get; set; }
            internal XElement Root { get; set; }
        }

    }


    public class TableFactory
    {
        private Dictionary<Type, XmlTable> Tables = new Dictionary<Type, XmlTable>();

        public TableFactory()
        {
        }

        public XmlTable GetTable<T>(string path)
        {
            var type = typeof(T);
            XmlTable t;
            if (Tables.Keys.Contains(type))
            {
                t = Tables[type];
                if (t.Root == null)
                    t.Root = Load(t.FileName);
            }
            else
            {
                t = CreateTable<T>(path);
                Tables.Add(type, t);
            }
            return t;
        }

        public XElement GetRoot<T>(string path)
        {
            XmlTable t = GetTable<T>(path);
            return t.Root;
        }

        public XmlTable CreateTable<T>(string path)
        {
            string className = BLReflection.GetClassName<T>();
            string fileName = path + className + ".xml";
            return new XmlTable { Name = className, FileName = fileName, Root = Load(fileName) };
        }

        public XmlTable GetTable(string path, Type type)
        {
            XmlTable t;
            if (Tables.Keys.Contains(type))
            {
                t = Tables[type];
                if (t.Root == null)
                    t.Root = Load(t.FileName);
            }
            else
            {
                t = CreateTable(path, type);
                Tables.Add(type, t);
            }
            return t;
        }

        public XElement GetRoot(string path, Type type)
        {
            XmlTable t = GetTable(path, type);
            return t.Root;
        }

        public XmlTable CreateTable(string path, Type type)
        {
            string className = type.Name;
            string fileName = path + className + ".xml";
            return new XmlTable { Name = className, FileName = fileName, Root = Load(fileName) };
        }

        public XElement Load(string fileName)
        {
            XDocument xd;
            if (!System.IO.File.Exists(fileName))
            {
                xd = XDocument.Parse("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
                       "<Sheet CreateDate=\"" + System.DateTime.Now + "\">" +
                        "</Sheet>"
                   );
            }
            else
                xd = XDocument.Load(fileName);
            return xd.Element("Sheet");
        }

        private static TableFactory _instance;

        public static TableFactory Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new TableFactory();
                }
                return _instance;
            }
        }


    }

    public class XmlTable
    {
        public string Name { get; set; }
        public string FileName { get; set; }
        public XElement Root { get; set; }
    }




}
